/*
 * Created by Jaren at 2021/8/21 14:39
 */
import 'dart:typed_data';

import 'package:inmotion_protocol/base/codec.dart';
import 'package:inmotion_protocol/ezcan/ezcan_constant.dart';
import 'package:inmotion_protocol/ezcan/ezcan_message.dart';
import 'package:inmotion_protocol/ezcan/util.dart';
import 'package:inmotion_protocol/ezcan/factory/ezcan_factory.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/date_time_util.dart';
import 'package:inmotion_protocol/util/int8_list_buffer.dart';
import 'package:inmotion_protocol/util/log.dart';

class EzcanDecoder extends Decoder<EzcanMessage> {
  Uint8ClampedList? lastDecodeBytes;
  int lastDecodeTimestampMicroSecond = 0;

  static Uint8ClampedList notSkipBytes = Uint8ClampedList(20);

  EzcanDecoder() {
    notSkipBytes = Uint8ClampedList.fromList([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
  }

  @override
  List<EzcanMessage> decode(Uint8ClampedList? bytes, List<EzcanMessage> output) {
    int now = DateTimeUtil.current(microSecond: true);
    if (now - lastDecodeTimestampMicroSecond <= 1500 &&
        lastDecodeBytes != null &&
        bytes != null &&
        ByteUtil.arrayEquals(lastDecodeBytes!, bytes) &&
        !ByteUtil.arrayEquals(notSkipBytes, bytes)) {
      LogUtils.d("Decode with same bytes too frequently, skip it.");
      return output;
    }
    lastDecodeTimestampMicroSecond = DateTimeUtil.current(microSecond: true);
    lastDecodeBytes = bytes;
    return super.decode(bytes, output);
  }

  @override
  bool isStartWithPacketHead(Uint8ClampedList bytes) {
    return ByteUtil.byteEquals(bytes[0], EzcanConstant.HEAD1) && ByteUtil.byteEquals(bytes[1], EzcanConstant.HEAD2);
  }

  @override
  int findValidPacketEndIndex(Int8ListBuffer buffer) {
    int oldReaderIndex = buffer.readerIndex;

    buffer.readerIndex = 0;
    int totalLength = buffer.readableBytes();
    if (totalLength < EzcanMessage.sMinLength) {
      LogUtils.e("findValidPacketEndIndex", "Total length in buffer is less than min length[" + EzcanMessage.sMinLength.toString() + "].");
      // 恢复readerIndex
      buffer.readerIndex = oldReaderIndex;
      return -1;
    }
    int index = 0;
    index += 2; // 2byte head
    index += 4; // 4byte id
    buffer.readerIndex = index;
    Uint8ClampedList data = Uint8ClampedList(8);
    buffer.readBytes(data, 0, 8);
    index += 8;
    buffer.readerIndex = index;
    int dataLength = buffer.readByte();
    index++; // 1byte data length
    index++; // 1byte channel
    buffer.readerIndex = index;
    int format = buffer.readByte();
    index++;
    int extraLength = 0;
    if (ByteUtil.byteEquals(format, EzcanConstant.FORMAT_EXTRA) && ByteUtil.byteEquals(dataLength, EzcanConstant.LENGTH_FLAG_EXTRA)) {
      extraLength = ByteUtil.bytes2UIntLe(data, 0, 4);
      index += extraLength;
    }
    index++; // 1byte type
    index++; // 1byte check
    index += 2; // 2byte tail

    buffer.readerIndex = oldReaderIndex;

    int expectLength = index;

    if (expectLength == totalLength) {
      return 0;
    }
    if (totalLength < expectLength) {
      return -1;
    }
    return buffer.readableBytes();
  }

  @override
  int minMsgLength() {
    return EzcanMessage.sMinLength;
  }

  bool lastEscapeChar = false;

  @override
  Uint8ClampedList unescape(Uint8ClampedList bytes) {
    // 是否存在转义字符标志位
    bool hasEscapeChar = false;

    Int8ListBuffer buf = Int8ListBuffer();
    // 上个转义字符的index
    int lastEscapeIndex = -99999;
    for (int i = 0, length = bytes.length; i < length; i++) {
      if (ByteUtil.byteEquals(bytes[i], EzcanConstant.ESCAPE_CHAR)) {
        LogUtils.d("lastEscapeIndex->" + lastEscapeIndex.toString() + ", i->" + i.toString());
        if (lastEscapeIndex + 1 != i) {
          if (i == 0 && lastEscapeChar) {
            // 第一个字符为转义字符a5，且上次转义的最后一个字符也为转义字符a5
            // 保留这个a5
            LogUtils.d("上次转义的最后一个字符为转义字符，本包首字节转义字符保留");
            lastEscapeChar = false;
          } else {
            hasEscapeChar = true;
            lastEscapeIndex = i;
            lastEscapeChar = false;
            LogUtils.d("转义字符，跳过");
            if (i == length - 1) {
              // 最后一个字符标记
              lastEscapeChar = true;
              LogUtils.d("转义字符，最后一个字符");
            }
            continue;
          }
        }
        LogUtils.d("连着2个转义字符，保留");
      }
      buf.writeByte(bytes[i]);
    }

    // 若原bytes中没有转义字符，直接返回
    if (!hasEscapeChar) {
      return bytes;
    }
    Uint8ClampedList result = Uint8ClampedList(buf.readableBytes());
    buf.readBytes(result);
    return result;
  }

  @override
  EzcanMessage parse(Uint8ClampedList buf) {
    int index = 0;
    index += 2; // 跳过2byte的头
    int id = ByteUtil.bytes2Uint32Le(buf, index).value;
    index += 4;
    /** more detail for length of data see {@link EzcanMsg#data} */
    Uint8ClampedList data = buf.sublist(index, index + 8);
    index += 8;
    int length = buf[index];
    index++;
    int channel = buf[index];
    index++;
    int format = buf[index];
    index++;
    int type = buf[index];
    index++;
    Uint8ClampedList? extra;
    if (ByteUtil.byteEquals(length, 0xfe)) {
      // 存在扩展数据
      /** more detail for extra data see {@link EzcanMsg#length}*/
      int extraLength = ByteUtil.bytes2UIntLe(data, 0, 4);

      extra = buf.sublist(index, index + extraLength);
      index += extraLength;
    }
    int check = buf[index];
    index++;
    index += 2; // 消息尾2byte

    return EzcanFactory.genMsg(id, data, length, channel, format, type, extra, check);
  }

  @override
  void decrypt(Int8ListBuffer bytes) {}

  @override
  bool check(Uint8ClampedList bytes) {
    int check = EzcanUtil.sumCheck1Byte(bytes, 2, bytes.length - 2 - 2 - 1);
    int checkFromMsg = bytes[bytes.length - 2 - 1];

    return ByteUtil.byteEquals(check, checkFromMsg);
  }

  @override
  int findPacketHead(Uint8ClampedList bytes) {
    int lastHead1Index = -1;
    for (int i = 0; i < bytes.length; i++) {
      if (ByteUtil.byteEquals(bytes[i], EzcanConstant.HEAD1)) {
        if (lastHead1Index >= 0) {
          return lastHead1Index;
        } else {
          lastHead1Index = i;
        }
      } else {
        lastHead1Index = -1;
      }
    }
    return -1;
  }
}

class EzcanEncoder extends Encoder<EzcanMessage> {
  @override
  Uint8ClampedList msgToBytes(EzcanMessage msg) {
    buffer.clear();

    buffer.writeBytes(msg.head);
    buffer.writeBytes(ByteUtil.num2BytesLe(msg.id.value, 4));
    buffer.writeBytes(msg.data);
    buffer.writeByte(msg.length.value);
    buffer.writeByte(msg.channel.value);
    buffer.writeByte(msg.format.value);
    buffer.writeByte(msg.type.value);

    if (msg.extra != null) {
      buffer.writeBytes(msg.extra!);
    }

    buffer.writeByte(msg.check!.value);
    buffer.writeBytes(msg.tail);

    Uint8ClampedList bytes = Uint8ClampedList(buffer.readableBytes());
    buffer.readBytes(bytes);
    // 转义
    return escape(bytes);
  }

  Int8ListBuffer sBufForEscape = Int8ListBuffer();

  Uint8ClampedList escape(Uint8ClampedList bytes) {
    sBufForEscape.clear();

    // 包头不参与转义
    sBufForEscape.writeByte(bytes[0]);
    sBufForEscape.writeByte(bytes[1]);

    // 参与转义的长度length-2-2（从包头后算起，除包尾）
    for (int i = 2, escapeLength = bytes.length - 2; i < escapeLength; i++) {
      if (ByteUtil.byteEquals(bytes[i], EzcanConstant.CHAR1_NEED_ESCAPE) ||
          ByteUtil.byteEquals(bytes[i], EzcanConstant.CHAR2_NEED_ESCAPE) ||
          ByteUtil.byteEquals(bytes[i], EzcanConstant.CHAR3_NEED_ESCAPE)) {
        sBufForEscape.writeByte(EzcanConstant.ESCAPE_CHAR);
      }
      sBufForEscape.writeByte(bytes[i]);
    }

    // 添加包尾
    sBufForEscape.writeByte(bytes[bytes.length - 2]);
    sBufForEscape.writeByte(bytes[bytes.length - 1]);

    Uint8ClampedList bytesAfterEscape = Uint8ClampedList(sBufForEscape.readableBytes());
    sBufForEscape.readBytes(bytesAfterEscape);
    return bytesAfterEscape;
  }

  @override
  Uint8ClampedList encrypt(Uint8ClampedList bytes) {
    // nothing to do
    return bytes;
  }

  @override
  bool needEncrypt(EzcanMessage msg) {
    return false;
  }
}
